package com.wuhao.jdqlibnoui.ble

import android.annotation.SuppressLint
import android.app.Service
import android.bluetooth.BluetoothAdapter
import android.bluetooth.BluetoothGatt
import android.bluetooth.BluetoothGattCallback
import android.bluetooth.BluetoothGattCharacteristic
import android.bluetooth.BluetoothGattDescriptor
import android.bluetooth.BluetoothGattService
import android.bluetooth.BluetoothManager
import android.bluetooth.BluetoothProfile
import android.content.Intent
import android.os.Binder
import android.os.IBinder
import android.util.Log
import com.drake.channel.sendEvent
import com.wuhao.jdqlibnoui.constants.AppConfig
import com.wuhao.jdqlibnoui.constants.BleConfig
import com.wuhao.jdqlibnoui.data.BleReceiveDataEvent
import com.wuhao.jdqlibnoui.data.BleStatusChangeEvent
import com.wuhao.jdqlibnoui.utils.DataUtils
import com.wuhao.jdqlibnoui.utils.LogUtil

/**
 * Service for managing connection and data communication with a GATT server hosted on a
 * given Bluetooth LE device.
 */
@SuppressLint("MissingPermission")
class UartServiceUART : Service() {
    private var mBluetoothManager: BluetoothManager? = null
    private var mBluetoothAdapter: BluetoothAdapter? = null
    private var mBluetoothDeviceAddress: String? = null
    private var mBluetoothGatt: BluetoothGatt? = null

    private var mBluetoothDeviceName: String? = null

    // Implements callback methods for GATT events that the app cares about.  For example,
    // connection change and services discovered.
    private val mGattCallback: BluetoothGattCallback = object : BluetoothGattCallback() {
        override fun onConnectionStateChange(gatt: BluetoothGatt, status: Int, newState: Int) {
            val intentAction: String

            if (newState == BluetoothProfile.STATE_CONNECTED) {
                intentAction = BleConfig.BLE_CONNECTED
                broadcastUpdate(intentAction)
                Log.i(TAG, "Connected to GATT server.")
                // Attempts to discover services after successful connection.
                Log.i(
                    TAG, "Attempting to start service discovery:" +
                            mBluetoothGatt!!.discoverServices()
                )
            } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
                intentAction = BleConfig.BLE_DISCONNECTED
                Log.i(TAG, "Disconnected from GATT server.")
                broadcastUpdate(intentAction)
            }
        }

        override fun onServicesDiscovered(gatt: BluetoothGatt, status: Int) {
            if (status == BluetoothGatt.GATT_SUCCESS) {
                Log.w(TAG, "mBluetoothGatt = $mBluetoothGatt")

                broadcastUpdate(BleConfig.BLE_SERVICES_DISCOVERED)
            } else {
                Log.w(TAG, "onServicesDiscovered received: $status")
            }
        }

        override fun onCharacteristicRead(
            gatt: BluetoothGatt,
            characteristic: BluetoothGattCharacteristic,
            status: Int
        ) {
            if (status == BluetoothGatt.GATT_SUCCESS) {
                broadcastUpdate(characteristic)
            }
        }

        override fun onCharacteristicChanged(
            gatt: BluetoothGatt,
            characteristic: BluetoothGattCharacteristic
        ) {
            broadcastUpdate(characteristic)
        }
    }

    fun setMTU(mtu: Int) {
        try {
            val result = mBluetoothGatt?.requestMtu(mtu)
            LogUtil.error("setMtu:$result")
        } catch (e: Exception) {
            e.printStackTrace()
        }
    }

    private fun broadcastUpdate(action: String) {
        sendEvent(BleStatusChangeEvent(action))
    }

    private fun broadcastUpdate(
        characteristic: BluetoothGattCharacteristic
    ) {
        // This is handling for the notification on TX Character of NUS service
        if (AppConfig.bleNotifyUUID == characteristic.uuid) {
            // Log.d(TAG, String.format("Received TX: %d",characteristic.getValue() ));
            if(AppConfig.bleServiceUUID == BleConfig.RX_SERVICE_UUID2){
                //新记弹器
                sendEvent(BleReceiveDataEvent(DataUtils.bytesToHexString(characteristic.value)))
            }else{
                sendEvent(BleReceiveDataEvent(String(characteristic.value, Charsets.UTF_8)))
            }
        }
    }

    inner class LocalBinder : Binder() {
        val service: UartServiceUART
            get() = this@UartServiceUART
    }

    override fun onBind(intent: Intent): IBinder? {
        return mBinder
    }

    override fun onUnbind(intent: Intent): Boolean {
        // After using a given device, you should make sure that BluetoothGatt.close() is called
        // such that resources are cleaned up properly.  In this particular example, close() is
        // invoked when the UI is disconnected from the Service.
        close()
        return super.onUnbind(intent)
    }

    private val mBinder: IBinder = LocalBinder()

    /**
     * Initializes a reference to the local Bluetooth adapter.
     *
     * @return Return true if the initialization is successful.
     */
    fun initialize(): Boolean {
        // For API level 18 and above, get a reference to BluetoothAdapter through
        // BluetoothManager.
        if (mBluetoothManager == null) {
            mBluetoothManager = getSystemService(BLUETOOTH_SERVICE) as BluetoothManager
            if (mBluetoothManager == null) {
                Log.e(TAG, "Unable to initialize BluetoothManager.")
                return false
            }
        }

        mBluetoothAdapter = mBluetoothManager!!.adapter
        if (mBluetoothAdapter == null) {
            Log.e(TAG, "Unable to obtain a BluetoothAdapter.")
            return false
        }

        return true
    }

    /**
     * Connects to the GATT server hosted on the Bluetooth LE device.
     *
     * @param address The device address of the destination device.
     *
     * @return Return true if the connection is initiated successfully. The connection result
     * is reported asynchronously through the
     * `BluetoothGattCallback#onConnectionStateChange(android.bluetooth.BluetoothGatt, int, int)`
     * callback.
     */
    fun connect(address: String?): Boolean {
        if (mBluetoothAdapter == null || address == null) {
            Log.w(TAG, "BluetoothAdapter not initialized or unspecified address.")
            return false
        }

        // Previously connected device.  Try to reconnect.
        if (mBluetoothDeviceAddress != null && address == mBluetoothDeviceAddress && mBluetoothGatt != null) {
            Log.d(TAG, "Trying to use an existing mBluetoothGatt for connection.")
            return if (mBluetoothGatt!!.connect()) {
                true
            } else {
                false
            }
        }

        val device = mBluetoothAdapter!!.getRemoteDevice(address)
        if (device == null) {
            Log.w(TAG, "Device not found.  Unable to connect.")
            return false
        }
        // We want to directly connect to the device, so we are setting the autoConnect
        // parameter to false.
        mBluetoothGatt = device.connectGatt(this, false, mGattCallback)
        Log.d(TAG, "Trying to create a new connection.")
        mBluetoothDeviceAddress = address
        return true
    }

    /**
     * Disconnects an existing connection or cancel a pending connection. The disconnection result
     * is reported asynchronously through the
     * `BluetoothGattCallback#onConnectionStateChange(android.bluetooth.BluetoothGatt, int, int)`
     * callback.
     */
    fun disconnect(): Boolean {
        if (mBluetoothAdapter == null || mBluetoothGatt == null) {
            Log.w(TAG, "BluetoothAdapter not initialized")
            return false
        }
        mBluetoothGatt!!.disconnect()
        // mBluetoothGatt.close();
        return true
    }

    /**
     * After using a given BLE device, the app must call this method to ensure resources are
     * released properly.
     */
    fun close() {
        if (mBluetoothGatt == null) {
            return
        }
        Log.w(TAG, "mBluetoothGatt closed")
        mBluetoothDeviceAddress = null
        mBluetoothGatt!!.close()
        mBluetoothGatt = null
    }

    /**
     * Request a read on a given `BluetoothGattCharacteristic`. The read result is reported
     * asynchronously through the `BluetoothGattCallback#onCharacteristicRead(android.bluetooth.BluetoothGatt, android.bluetooth.BluetoothGattCharacteristic, int)`
     * callback.
     *
     * @param characteristic The characteristic to read from.
     */
    fun readCharacteristic(characteristic: BluetoothGattCharacteristic?) {
        if (mBluetoothAdapter == null || mBluetoothGatt == null) {
            Log.w(TAG, "BluetoothAdapter not initialized")
            return
        }
        mBluetoothGatt!!.readCharacteristic(characteristic)
    }

    /**
     * Enables or disables notification on a give characteristic.
     *
     *
     */
    /**
     * Enable Notification on TX characteristic
     *
     * @return
     */
    fun enableTXNotification() {
        /*
    	if (mBluetoothGatt == null) {
    		showMessage("mBluetoothGatt null" + mBluetoothGatt);
    		broadcastUpdate(DEVICE_DOES_NOT_SUPPORT_UART);
    		return;
    	}
    		*/
        val RxService = mBluetoothGatt!!.getService(AppConfig.bleServiceUUID)
        if (RxService == null) {
            showMessage("Rx service not found!")
            broadcastUpdate(BleConfig.DEVICE_DOES_NOT_SUPPORT_UART)
            return
        }
        val TxChar = RxService.getCharacteristic(AppConfig.bleNotifyUUID)
        if (TxChar == null) {
            showMessage("Tx charateristic not found!")
            broadcastUpdate(BleConfig.DEVICE_DOES_NOT_SUPPORT_UART)
            return
        }
        mBluetoothGatt!!.setCharacteristicNotification(TxChar, true)

        val descriptor = TxChar.getDescriptor(AppConfig.bleTypeUUID)
        descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE)
        mBluetoothGatt!!.writeDescriptor(descriptor)
    }


    fun writeRXCharacteristic(value: ByteArray?): Boolean {
        try {

            Log.e("lpz11", "writeRXCharacteristic: " + DataUtils.bytesToHexString(value))

            val rxService = mBluetoothGatt!!.getService(AppConfig.bleServiceUUID)
            showMessage("mBluetoothGatt null$mBluetoothGatt")
            if (rxService == null) {
                showMessage("Rx service not found!")
                broadcastUpdate(BleConfig.DEVICE_DOES_NOT_SUPPORT_UART)
                return false
            }
            val rxChar = rxService.getCharacteristic(AppConfig.bleWriteUUID)
            if (rxChar == null) {
                showMessage("Rx charateristic not found!")
                broadcastUpdate(BleConfig.DEVICE_DOES_NOT_SUPPORT_UART)
                return false
            }
            rxChar.setValue(value)
            val status = mBluetoothGatt!!.writeCharacteristic(rxChar)
            Log.d("lpz11", "write TXchar - status=$status")
            return status
        } catch (e: Exception) {
            e.printStackTrace()
            return false
        }
    }

    private fun showMessage(msg: String) {
        Log.e(TAG, msg)
    }

    val supportedGattServices: List<BluetoothGattService>?
        /**
         * Retrieves a list of supported GATT services on the connected device. This should be
         * invoked only after `BluetoothGatt#discoverServices()` completes successfully.
         *
         * @return A `List` of supported services.
         */
        get() {
            if (mBluetoothGatt == null) return null

            return mBluetoothGatt!!.services
        }



    private fun Byte.toHex(): String = "%02X".format(this)

    private fun ByteArray.toHexString(): String =
        joinToString(separator = " ") { byte -> byte.toHex() }


    companion object {
        private val TAG: String = UartServiceUART::class.java.simpleName
    }
}
